package
{
	import com.adobe.utils.*;
	import com.barliesque.agal.EasyAGAL;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DTriangleFace;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.*;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.utils.ByteArray;
	
	

	[SWF(width="640",height="480",frameRate="60",backgroundColor="#FFFFFF")]
	public class Main3D extends Sprite
	{
		protected const _swfWidth:int=640;
		protected const _swfHeight:int=480;
		protected var _context3d:Context3D;
		protected var _shaderProgram:Program3D;
		protected var _stage3d:Stage3D;
		
		protected var _backgroundColor:Vector.<Number> = new <Number>[1,1,1];
		
		//vertexts,indexs数据
		protected var _vertexBuffer:VertexBuffer3D;
		protected var _indexBuffer:IndexBuffer3D;
		protected var _meshVertexData:Vector.<Number>;
		protected var _meshVertexDataLength:uint;
		protected var _meshIndexData:Vector.<uint>;
		
		//变换矩阵
		protected var _projectionMatrix:PerspectiveMatrix3D = new PerspectiveMatrix3D();
		protected var _viewMatrix:Matrix3D = new Matrix3D();
		protected var _worldMatrix:Matrix3D = new Matrix3D;
		protected var _modelViewProjction:Matrix3D = new Matrix3D();

		//easy agal
		private var _shader:EasyAGAL;
		public function Main3D()
		{
			if(stage != null)
				init3DSpace();
			else addEventListener(Event.ADDED_TO_STAGE,init3DSpace);
		}
		private function init3DSpace(e:Event=null):void
		{
			if(this.hasEventListener(Event.ADDED_TO_STAGE))
				removeEventListener(Event.ADDED_TO_STAGE,init3DSpace);
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			//requset Context3D
			prepareStage3d();
		}
		private function prepareStage3d():void
		{
			if(stage.stage3Ds.length > 0)
			{
				_stage3d = stage.stage3Ds[0];
				_stage3d.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate);
				//创建context3d对象的请求
				_stage3d.requestContext3D();
			}

		}
		private function onContext3DCreate(e:Event):void
		{
//			//使得enterframe前init context3d
			if(hasEventListener(Event.ENTER_FRAME))
				removeEventListener(Event.ENTER_FRAME,render);
			var t:Stage3D = e.target as Stage3D;
			this._context3d = t.context3D;
			
			if(!_context3d)
				return;
			_context3d.enableErrorChecking = true;

			//设置渲染缓冲区的视口尺寸和其他属性。 
			_context3d.configureBackBuffer(_swfWidth,_swfHeight,2,true);
			_context3d.setCulling(Context3DTriangleFace.BACK);			
			
			main();
			addEventListener(Event.ENTER_FRAME,render);
		}
		
		/**need to be overrided**/
		protected function main():void
		{
			
		}
		/**need to be overrided**/
		protected function draw():void
		{
			
		}
		protected function render(e:Event):void
		{
			_context3d.clear(_backgroundColor[0],_backgroundColor[1],_backgroundColor[2]);
			draw();
			_context3d.present();
		}
		
		//创建mipmap
		public function generateMipMap(bitMap:Bitmap,size:Number):Texture
		{
			var myTexture:Texture = _context3d.createTexture(size,size,Context3DTextureFormat.BGRA,false);
			var ws:int = bitMap.bitmapData.width;
			var hs:int = bitMap.bitmapData.height;
			var level:int=0;
			var tmp:BitmapData;
			var transform:Matrix = new Matrix;
			tmp = new BitmapData(ws,hs,true,0x00000000);
			while(ws >=1 && hs>=1)
			{
				tmp.draw(bitMap.bitmapData,transform,null,null,null,true);
				myTexture.uploadFromBitmapData(tmp,level);
				transform.scale(0.5,0.5);
				level++;
				ws>>=1;
				hs>>=1;
				if(hs && ws)
				{
					tmp.dispose();
					tmp = new BitmapData(ws,hs,true,0x00000000);
				}				
			}
			tmp.dispose();
			return myTexture;
		}
		
		
		public function addLabel(pMessage:String):void
		{
			var format:TextFormat = new TextFormat("Courier New",20,0xffffff,true);
			format.align = TextFormatAlign.CENTER;
			var field:TextField = new TextField();
			field.autoSize = TextFieldAutoSize.LEFT;
			field.defaultTextFormat = format;
			field.selectable = false;
			field.mouseEnabled = false;
			field.multiline = true;
			field.wordWrap = false;
			field.width = 1;
			field.height = 20;
			field.htmlText = pMessage;
			field.x = stage.stageWidth - field.width>>1;
			field.y = stage.stageHeight - field.height>>1;
			addChild(field);
		}
		
		//创建 Program3D 对象并upload
		public function createProgram(pVertexSrc:String,pFragmentSrc:String):void
		{
			_shaderProgram = _context3d.createProgram();
			_shaderProgram.upload(assembleVertex(pVertexSrc),assembleFragment(pFragmentSrc));
		}
		
		public function get program():Program3D
		{
			return _shaderProgram;
		}
		public function set program(value:Program3D):void
		{
			_shaderProgram = value;
			_context3d.setProgram(_shaderProgram);
		}
		
		private function assemble(pString:String,pKind:String):ByteArray
		{
			var agal:AGALMiniAssembler = new AGALMiniAssembler;
			return agal.assemble(pKind,pString.replace(/\|/g,"\n"));
		}
		public function assembleVertex(pSource:String):ByteArray
		{
			return assemble(pSource,Context3DProgramType.VERTEX);
		}
		public function assembleFragment(pSource:String):ByteArray
		{
			return assemble(pSource,Context3DProgramType.FRAGMENT);
		}
		public function get backgroundColor():uint
		{
			var r:uint = _backgroundColor[0]*255;
			var g:uint = _backgroundColor[1]*255;
			var b:uint = _backgroundColor[2]*255;
			return r<<16 | b<<8 | b;
		}
		public function set backgroundColor(value:uint):void 
		{             
			_backgroundColor[0] =   ((value >> 16) & 0xFF) / 255;            
			_backgroundColor[1] =   ((value >> 8 ) & 0xFF) / 255;             
			_backgroundColor[2] =   (value & 0xFF) / 255;         
		}
		public function get indexData():Vector.<uint>
		{
			return _meshIndexData;
		}
		public function setIndexData(value:Vector.<uint>):void
		{
			_meshIndexData = value;
			if(_indexBuffer)
				_indexBuffer.dispose();
			//创建索引缓冲区
			_indexBuffer = _context3d.createIndexBuffer(_meshIndexData.length);
			//填充索引缓冲区
			_indexBuffer.uploadFromVector(_meshIndexData,0,_meshIndexData.length);
		}
		
		//最多可以为每个顶点指定 64 个 32 位值。（着色器程序最多可以使用 8 个输入。）
		public function setVertexData(pDataLength:int,pVertexData:Vector.<Number>):void
		{
			_meshVertexDataLength = pDataLength;
			_meshVertexData = pVertexData;
			
			if(_vertexBuffer)
				_vertexBuffer.dispose();
			
			//创建顶点缓冲区
			_vertexBuffer = _context3d.createVertexBuffer(numberOfVertices,_meshVertexDataLength);
			//填充顶点缓冲区
			_vertexBuffer.uploadFromVector(_meshVertexData,0,numberOfVertices);
		}
		public function get numberOfIndices():int
		{
			return !_meshIndexData ? 0 : _meshIndexData.length/3;
		}
		public function get numberOfVertices():int
		{
			return !_meshVertexData || _meshVertexData.length<=0 ? 0 : _meshVertexData.length/_meshVertexDataLength;
		}
		
		public function get stage3D():Stage3D
		{
			return _stage3d;
		}
		
		public function get context3D():Context3D
		{
			return _context3d;
		}
		
		public function get shader():EasyAGAL
		{
			return _shader;
		}
		public function set shader(value:EasyAGAL):void
		{
			_shader = value;
			program = _shader.upload(context3D);
		}

	}

}